/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package client.controllers;

import client.gui.controls.scrabble.Hokje;
import client.gui.controls.scrabble.Blokje;
import client.gui.controls.scrabble.Vakje;
import client.data.ScoreEnWoord;
import client.gui.*;
import client.communication.ClientCommunication;
import java.awt.Point;
import java.util.ArrayList;
//import java.lang.reflect.Array;
import java.util.Enumeration;
import java.util.Hashtable;
import org.apache.log4j.Logger;
import java.awt.Color;

/**
 *
 * @author Groep 5
 */
public class ScrabbleController {

    private static Logger logger = Logger.getLogger(ScrabbleController.class);

    public void setBlokjesGevalideerd() {
        //blokjes gevalideerd zetten
        Point tempP;
        Enumeration<Point> e = getBezetteVakjes().keys();
        while (e.hasMoreElements()) {
            tempP = e.nextElement();
            Hokje h = moBezetteVakjes.get(tempP);
            Blokje blokje = (Blokje) h.getVakjeOfBlokje();
            blokje.setGevalideerd(true);
            moBezetteVakjes.remove(tempP);
            
        }
        moBezetteVakjes.clear();
        //eersteWoord=true;

        //de array aanmaken van woorden en berekenen van de score van de speler
    }

    private WoordRichting GeefRichting() {
        
        //wanneer er maar 1 letter gelegd is kan je niet adhv van de gelgde letters bepalen of 
        //het gelegde woord horizontaal of vertikaal ligt.
     //if (getBezetteVakjes().size() >= 1) 
    // {
      
        int x = 0;
        int y = 0;
        int i = 0;
        WoordRichting eRichting = WoordRichting.FOUTIEF;
        Point[] a = new Point[getBezetteVakjes().size()];
        getBezetteVakjes().keySet().toArray(a);
        int xkleinste = a[i].x;
        int xgrootste = a[i].x;
        int ykleinste = a[i].y;
        int ygrootste = a[i].y;

        while (i < a.length) {
            // we testen of x hetzelfde was als de vorige als dit zo is
            //tellen we bij x op
            if (x == a[i].x * i) {
                x += a[i].x;

                //System.err.println(x);
            } else {
                if (xkleinste > a[i].x) {
                    xkleinste = a[i].x;
                }
                if (xgrootste < a[i].x) {
                    xgrootste = a[i].x;
                }
            }

            // we testen of y hetzelfde was als de vorige als dit zo is
            //tellen we bij y op
            if (y == a[i].y * i) {
                y += a[i].y;
            } else {
                if (ykleinste > a[i].y) {
                    ykleinste = a[i].y;
                }
                if (ygrootste < a[i].y) {
                    ygrootste = a[i].y;
                }
            }
            i++;
        }
        // we hebben in de vorige functie x en y steeds opgeteld als ze gelijk bleven
        //nu testen we welk van de 2 over de ganse lijn gelijk waren als er 1 is dan
        //kennen we de richting van het woord anders hebben we een foutief woord
        if (x == getBezetteVakjes().size() * a[0].x) {
           // addNietGevalideerdeVakjes();
            int k = ykleinste + 1;
            while (k < ygrootste && moVakjes[a[0].x][k] != null) {
                k++;
            }
            if (k >= ygrootste) {
                eRichting = WoordRichting.HORIZONTAAL;
            } else {
                //logger.info("remove in valideerVakjes 1 ");
               //removeNietGevalideerdeBlokjes();
                eRichting = WoordRichting.FOUTIEF;
            }
        } else if (y == getBezetteVakjes().size() * a[0].y) {
            //addNietGevalideerdeVakjes();
            int k = xkleinste + 1;
            while (k < xgrootste && moVakjes[k][a[0].y] != null) {
                k++;
            }
            if (k >= xgrootste) {
                eRichting = WoordRichting.VERTIKAAL;
            } else {
                //logger.info("remove in valideerVakjes 2");
                //removeNietGevalideerdeBlokjes();
                eRichting = WoordRichting.FOUTIEF;
            }
        } else {
            //logger.info("remove in valideerVakjes 3");
           //removeNietGevalideerdeBlokjes();
            eRichting = WoordRichting.FOUTIEF;
        }
      // }

        logger.info("Richting: " + eRichting);
        return eRichting;
    }



    //richting van het woord is onbepaald wanneer de speler slechts 1 letter op het bord gelegd heeft
    public enum WoordRichting {

        HORIZONTAAL,
        VERTIKAAL,
        ONBEPAALD,
        FOUTIEF
    }
    //client server : enkel doorsturen naar client wanneer de blokjes op het spelbord worden gelegd
    //De andere clients zullen ook zien wanneer er neiuw blokjes worden gelegd.
    //het is niet nodig om bezette vakjes toe te voegen. enkel na het valideren van het woord zullen gevalideerde
    //vakjes toegevoegd worden. geen bezette vakjes nodig omdat de andere clients toch niks kunnen doen zolang de
    //speler die nu aan de beurt is zijn blokjes gevalideerd heeft of zijn beurt doorgegeven heeft.
    private Hokje[][] moVakjes = new Hokje[16][16];
    private Hashtable<Point, Hokje> moBezetteVakjes = new Hashtable<Point, Hokje>();
    //in hokje zit positie van het vakje
    private static ScrabbleController instance = new ScrabbleController();
    private int totaleScoreZet = 0;
    private boolean eersteWoord=true;
    
   public void reset() {
        for (int i=0; i<16; i++)
        {
            for (int j=0; j<16; j++)
            {
                moVakjes[i][j]=null;
            }
        }
        moBezetteVakjes.clear();
        totaleScoreZet = 0;
        eersteWoord=true;
    }
    public void setEersteWoord(boolean eersteWoord) {
        this.eersteWoord = eersteWoord;
    }

    public int getTotaleScoreZet() {
        return totaleScoreZet;
    }

    private ScrabbleController() {
    }

    private boolean eersteWoordOpSterretje() {
        boolean woordOpSterretje = false;

        for (Hokje h : moBezetteVakjes.values()) {
            int x = h.getLocatie().x;
            int y = h.getLocatie().y;

            if (x == 7 && y == 7) {
                woordOpSterretje = true;
                logger.info("Eerste woord ligt op sterretje = OK");            
            }
        }
        if (!woordOpSterretje)
              ClientCommunication.getInstance().getScrabbleClient().showMessage(">> Het eerste woord werd niet op het sterretje gelegd",Color.red);
        
        return woordOpSterretje;
    }

    //Methode die nagaat of het nieuw gelegde woord grenst aan de vorige woorden
    //zoniet worden de nieuw gelegde blokjes verwijderd
    private boolean checkNieuwWoordAangrenzend() {
        //Alle laatst gelegde (niet gevalideerde) blokjes overlopen
        boolean aangrenzend = false;
        ArrayList<Point> tempPointTab=new ArrayList();

        for (Hokje h : moBezetteVakjes.values()) {
           int x = h.getLocatie().x;
           int y = h.getLocatie().y;
            
           Point[] tempp=new Point[4];
           
           //dit zijn de 4 omliggende punten van het blokje
           tempp[0]= new Point(x,y+1);
           tempp[1]= new Point(x,y-1);
           tempp[2]= new Point(x+1,y);
           tempp[3]= new Point(x-1,y);
           
           
           for (int i=0;i<4;i++)
           {
               //ze worden enkel in de tabel van punten opgenomen als ze niet in het woord zelf voorkomen
               if(!moBezetteVakjes.containsKey(tempp[i]))
                   tempPointTab.add(tempp[i]);
              
           }
        }
        
        for (int i=0; i<tempPointTab.size();i++)
        {
            //de tabel wordt nu nagekeken of er een blokje op 1 van de omringende blokjes van het tijdelijke woord liggen
            //als dit zo is wordt aangrenzend op true gezet
            if(tempPointTab.get(i).x>=0&&tempPointTab.get(i).y>=0&& moVakjes[tempPointTab.get(i).x][tempPointTab.get(i).y]!=null)
                aangrenzend=true;
        }
                      
        if (!aangrenzend)
            ClientCommunication.getInstance().getScrabbleClient().showMessage(">> Het woord is niet aangrenzend met een reeds gelegd woord.",Color.red);
       

        logger.info("aangrenzend is: " +aangrenzend);
        return aangrenzend;
    }

    //aangezien alle blokjes in dezefde array zitten worden de niet
    // juiste blokjes eruit gehaald.
    public void removeNietGevalideerdVakje(Point p) {
        //x is y en y is x (ingewikkeld)
        if(moVakjes[p.x][p.y]!=null)
        {
                    Blokje b = (Blokje)moVakjes[p.x][p.y].getVakjeOfBlokje();
                    logger.info("Blokje [" + p.y + ";" + p.x + "] wordt van het spelbord verwijderd!");
                    ClientCommunication.getInstance().verplaatsBlokjeOpSpelbord(new Point(-1,-1),p,b.getLetter() ,b.isBlancoBlokje(), ClientCommunication.getInstance().getGameNaam());
                    moVakjes[p.x][p.y] = null;
       
                    Hokje h = moBezetteVakjes.get(p);
       
       
        //verwijdert blokje van veld 
        //Zet dit NIET in plankje
        //wanneer client server geimplementeerd
        //zal een bep klasse de info terug krijgen dat het woord niet gevalideerd is
        //alle gelegde letters worden terug gegeven en vanuit deze klasse kunnen deze blokjes dan terug toegevoegd 
        //worden aan het plankje via de methode plaatsBlokje()
        h.setVakjeOfBlokje(h.getVakje());
        }
    }

    public void addBezetVakje(Hokje v) {
        moBezetteVakjes.put(v.getLocatie(), v);
    }

    public void removeBezetVakje(Point p) {
        moBezetteVakjes.remove(p);
    }

    public ArrayList<String> valideerVakjes() {
        logger.info("start valideerVakjes");
        addNietGevalideerdeVakjes();
        WoordRichting eRichting = WoordRichting.FOUTIEF;
        boolean aangrenzend=false;
        boolean eersteWoordOpSter = true;
        Point[] b = new Point[getBezetteVakjes().size()];
        getBezetteVakjes().keySet().toArray(b);
        
        if(getBezetteVakjes().size()>0)
        {
            //als het niet het eerste woord is moet er gechecked worden of het woord 
            //aanliggend is
            if(!eersteWoord)
            {
                aangrenzend = checkNieuwWoordAangrenzend();
            }
            //als het het eerste woord is moet er nagekeken worden of het op het sterretje ligt
            else
            {
                 logger.info("Eerste woord gelegd.");
                 eersteWoordOpSter = eersteWoordOpSterretje();
            }

            //de woordrichting moet bepaald worden
            eRichting = GeefRichting();   

            //als de woordrichting foutief is wordt er niet verder gedaan
            if(eRichting== WoordRichting.FOUTIEF)
            {
                    removeNietGevalideerdeBlokjes();
                    ClientCommunication.getInstance().getScrabbleClient().showMessage(">> Je mag geen woorden leggen in beide richtingen!",Color.red);
                    return null;
            }
            else
            {   
                //als er maar 1 letter gelegd word als eerste woord dan is het woord zoiezo fout
                   if(moBezetteVakjes.size()==1 && eersteWoord )
                   {
                       ClientCommunication.getInstance().getScrabbleClient().showMessage(">> Een woord van 1 letter bestaat niet!",Color.red);
                            removeNietGevalideerdeBlokjes();
                            return null;
                   }

                    //Enkel als het woord aangrezend is, verder de woorden bepalen
                    //Als het eerste woord gelegd is, staat aantalWoorden nog op nul
                    //Aangezien bij het eerste woord er geen enkel woord is om aan te grenzen
                    //zal aangrenzend = false zijn, dus daarom extra check op aantalWoorden
                    if ((aangrenzend || eersteWoord) && eersteWoordOpSter) 
                    {
                        logger.info("Woord gelegd --> aangrenzend=" + aangrenzend + ", eersteWoord=" + eersteWoord + ", eersteWoordOpSter="+eersteWoordOpSter);
                        return bepaalWoorden(eRichting, b[0]);
                    } else {
                        //Als het woord niet grenst, alle gelegde blokjes verwijderen
                        //Als het eerste woord niet op het sterretje ligt, alle gelegde blokjes
                        //verwijderen
                        //en de bezette vakjes leegmaken
                        removeNietGevalideerdeBlokjes();
                        moBezetteVakjes.clear();
                }
            }
        }
        logger.info("geen woorden gevonden");
        return null;
    }

    private void addNietGevalideerdeVakjes() {
        Enumeration<Point> e = getBezetteVakjes().keys();
        Point tempP;
        while (e.hasMoreElements()) {
            tempP = e.nextElement();
            moVakjes[tempP.x][tempP.y] = moBezetteVakjes.get(tempP);
        }
    }
    
    public void addNietGevalideerdVakje(Hokje h)
    {
        moVakjes[h.getLocatie().x][h.getLocatie().y] = h;
    }
    
    public void removeNietGevalideerdVakje(Hokje h)
    {
        moVakjes[h.getLocatie().x][h.getLocatie().y] = null;
    }

    //verwacht een gesorteerde (volgens een bepaalde richting) tabel van coordinaten
    private ArrayList<String> bepaalWoorden(WoordRichting richting, Point p) {
        ArrayList<ScoreEnWoord> arlWoorden = new ArrayList<ScoreEnWoord>();
        ArrayList<String> goedeWoorden = new ArrayList<String>();
        ScoreEnWoord sew;
        totaleScoreZet = 0;

        //als het woord horizontaal ligt:
        if (richting == WoordRichting.HORIZONTAAL) {
            sew = bepaalWoordHorizontaal(p);
            if (sew.getWoord().length() >= 1) {
                arlWoorden.add(sew);
            }
            // alle blokjes zoeken en ALLE vertikale woorden zoeken
            Enumeration<Point> e = getBezetteVakjes().keys();
            while (e.hasMoreElements()) {
                //voor elk blokje kijken of er geen andere aangrenzende blokjes zijn
                //in vertikale richting
                //sWoord = bepaalWoordVertikaal(e.nextElement());

                sew = bepaalWoordVertikaal(e.nextElement());
                if (sew.getWoord().length() > 1) {
                    arlWoorden.add(sew);
                //sWoord = "";
                }
            }

        } //als het woord horizontaal ligt:
        else if (richting == WoordRichting.VERTIKAAL) {
            sew = bepaalWoordVertikaal(p);
            if (sew.getWoord().length() > 1) {
                arlWoorden.add(sew);
            //sWoord = "";
            }
            // alle blokjes zoeken en ALLE horizontaal woorden zoeken
            Enumeration<Point> e = getBezetteVakjes().keys();
            while (e.hasMoreElements()) {
                //voor elk blokje kijken of er geen andere aangrenzende blokjes zijn
                //in horizontale richting
                sew = bepaalWoordHorizontaal(e.nextElement());
                if (sew.getWoord().length() > 1) {
                    arlWoorden.add(sew);
                //sWoord = "";
                }
            }
        }
        
        for (int i = 0; i < arlWoorden.size(); i++) {
                goedeWoorden.add(arlWoorden.get(i).getWoord());
                logger.info("De score van het woord " + arlWoorden.get(i).getWoord() + " = " + arlWoorden.get(i).getScore());
                totaleScoreZet += arlWoorden.get(i).getScore();
        }

        logger.info("woordbepaling: "+ goedeWoorden.size());
        return goedeWoorden;
    }

    private ScoreEnWoord bepaalWoordVertikaal(Point p) {

        int x = p.x;
        int y = p.y;
        String sWoord = "";
        int score = 0;
        int tempScore = 0;
        boolean tweeMaalWW = false;
        boolean drieMaalWW = false;
        Hokje h;

        //zolang de bovenkant spelbord niet bereikt is of wanneer je een leeg vakje tegenkomt
        //zoeken naar vakjes waarop een blokje ligt
        while (x - 1 >= 0 && (moVakjes[x - 1][y] != null)) {
            x--;
        }
        //woord aflezen
        while (x < 16 && (moVakjes[x][y] != null)) {
            h = moVakjes[x][y];
            sWoord += h.getVakjeOfBlokje().getText();
            tempScore = ((Blokje) h.getVakjeOfBlokje()).getWaarde();
            Vakje.Type t = h.getVakje().getType();
            switch (t) {
                case DRIE_MAAL_WOORDWAARDE:
                    drieMaalWW = true;
                    break;
                case TWEE_MAAL_WOORDWAARDE:
                    tweeMaalWW = true;
                    break;
                case DRIE_MAAL_LETTERWAARDE:
                    tempScore = 3 * tempScore;
                    break;
                case TWEE_MAAL_LETTERWAARDE:
                    tempScore = 2 * tempScore;
                    break;
            }
            score += tempScore;

            x++;
        }

        if (tweeMaalWW) {
            score = 2 * score;
        }
        if (drieMaalWW) {
            score = 3 * score;
        }


        return new ScoreEnWoord(sWoord, score);

    }

    private ScoreEnWoord bepaalWoordHorizontaal(Point p) {
        int x = p.x;
        int y = p.y;

        //zolang de bovenkant spelbord niet bereikt is of wanneer je een leeg vakje tegenkomt
        //zoeken naar vakjes waarop een blokje ligt
        while (y - 1 >= 0 && (moVakjes[x][y - 1] != null)) {
            y--;
        }

        String sWoord = "";
        int score = 0;
        int tempScore = 0;
        boolean tweeMaalWW = false;
        boolean drieMaalWW = false;
        Hokje h;
        //woord aflezen
        while (y < 16 && (moVakjes[x][y] != null)) {
            h = moVakjes[x][y];
            sWoord += h.getVakjeOfBlokje().getText();
            tempScore = ((Blokje) h.getVakjeOfBlokje()).getWaarde();
            Vakje.Type t = h.getVakje().getType();
            switch (t) {
                case DRIE_MAAL_WOORDWAARDE:
                    drieMaalWW = true;
                    break;
                case TWEE_MAAL_WOORDWAARDE:
                    tweeMaalWW = true;
                    break;
                case DRIE_MAAL_LETTERWAARDE:
                    tempScore = 3 * tempScore;
                    break;
                case TWEE_MAAL_LETTERWAARDE:
                    tempScore = 2 * tempScore;
                    break;
            }
            score += tempScore;

            y++;
        }
        if (tweeMaalWW) {
            score = 2 * score;
        }
        if (drieMaalWW) {
            score = 3 * score;
        }

        return new ScoreEnWoord(sWoord, score);

    }

    public Hokje[][] getGevalideerdeVakjes() {
        return moVakjes;
    }

    public Hashtable<Point, Hokje> getBezetteVakjes() {
        return moBezetteVakjes;
    }

    public static ScrabbleController getInstance() {
        return instance;
    }

    public void removeNietGevalideerdeBlokjes() {

        Point tempP;
      
        Enumeration<Point> e = getBezetteVakjes().keys();
        while (e.hasMoreElements()) {
            tempP = e.nextElement();
             logger.info("Persoon aan de beurt = " + ClientCommunication.getInstance().isAanDeBeurt());
            if(ClientCommunication.getInstance().isAanDeBeurt())
            {
               
                 ClientCommunication.getInstance().getPlankje().plaatsBlokje((Blokje)moBezetteVakjes.get(tempP).getVakjeOfBlokje());
            
            }
            logger.info("Blokjes teruggeplaatst op plankje. Vakjes worden nu verwijderd...");
            removeNietGevalideerdVakje(tempP);
            moBezetteVakjes.remove(tempP);
        }
    }

}
